/**
 * 20220119：finalized by jordia
 */
import Method from "../../common/method";

/**
 * 层次包围树算法策略
 */
export default class BvhStrategy {
    constructor() {
        this._insertionStrategy = 0;
        this._balancingEnabled = false;
        this._tv0=new Float64Array(3);
        this._tv1=new Float64Array(3);
        this._tv2=new Float64Array(3);
        this._tb0=new Float64Array(6);
    }
    _getDescendingCost(c,ce,le,cmb,incrementalCost){
        Method.boxUnionBox(ce,le,cmb);
        let ey,ez;
        let descendingCost;
        if(c._height === 0) {
            ey = cmb[4] - cmb[1]; ez = cmb[5] - cmb[2];
            descendingCost = incrementalCost + ((cmb[3] - cmb[0]) * (ey + ez) + ey * ez) * 2;
        } else {
            ey = cmb[4] - cmb[1]; ez = cmb[5] - cmb[2];
            let ey1 = ce[4] - ce[1], ez1 = ce[5] - ce[2];
            descendingCost = incrementalCost + (((cmb[3] - cmb[0]) * (ey + ez) + ey * ez) * 2 - ((ce[3] - ce[0]) * (ey1 + ez1) + ey1 * ez1) * 2);
        }
        return descendingCost;
    }
    _decideInsertion(currentNode,leaf) {
        let le=leaf.size;
        switch(this._insertionStrategy) {
            case 0:
                let center=Method.addArray(le,le,this._tv0,0,3,0,3);
                let c1 = currentNode._children[0].size, c2 = currentNode._children[1].size;
                let diff1=Method.addArray(c1,c1,this._tv1,0,3,0,3);
                let diff2=Method.addArray(c2,c2,this._tv2,0,3,0,3);
                Method.subArray(diff1,center,diff1,0,0,0,3);
                Method.subArray(diff2,center,diff2,0,0,0,3);
                let l1=Method.multiplyArray(diff1,diff1,0,0,3);
                let l2=Method.multiplyArray(diff2,diff2,0,0,3);
                return l1<l2?0:1;
            case 1:
                let c11 = currentNode._children[0], c21 = currentNode._children[1];
                let c11e = c11.size, c21e = c21.size, ne=currentNode.size;
                let ey = ne[4] - ne[1], ez = ne[5] - ne[2];
                let comb=Method.boxUnionBox(ne,le,this._tb0);
                let ey1 = comb[4] - comb[1], ez1 = comb[5] - comb[2];
                let newArea = ((comb[3] - comb[0]) * (ey1 + ez1) + ey1 * ez1) * 2;
                let creatingCost = newArea * 2;
                let incrementalCost = (newArea - ((ne[3] - ne[0]) * (ey + ez) + ey * ez) * 2) * 2;
                let descendingCost1=this._getDescendingCost(c11,c11e,le,comb,incrementalCost);
                let descendingCost2=this._getDescendingCost(c21,c21e,le,comb,incrementalCost);
                if(creatingCost < descendingCost1) {
                    if(creatingCost < descendingCost2) {
                        return -1;
                    } else {
                        return 1;
                    }
                } else if(descendingCost1 < descendingCost2) {
                    return 0;
                } else {
                    return 1;
                }
            default:
                console.log("invalid BVH insertion strategy: " + this._insertionStrategy);
                return -1;
        }
    }
    _setLeavesDir(centerMean,lr,leaves,index0,index1){
        let mean = centerMean;
        while(true) {
            while(!(leaves[lr[0]].tmp[index0] <= mean)) ++lr[0];
            while(!(leaves[lr[1]].tmp[index1] >= mean)) --lr[1];
            if(lr[0] >= lr[1]) break;
            let tmp = leaves[lr[0]];
            leaves[lr[0]] = leaves[lr[1]];
            leaves[lr[1]] = tmp;
            ++lr[0];
            --lr[1];
        }
    }
    _splitLeaves(leaves,from,until) {
        let invN = 1.0 / (until - from);
        let centerMean=Method.fillValue(this._tv0,0,2,0);
        let _g = from;
        while(_g < until) {
            let leaf = leaves[_g++];
            let tmp=leaf.tmp,size=leaf.size;
            Method.addArray(size,size,tmp,3,0,0,3);
            Method.addArray(centerMean,tmp,centerMean,0,0,0,3);
        }
        Method.scaleArray(centerMean,invN,centerMean,0,0,3);
        let variance=Method.fillValue(this._tv1,0,2,0);
        let _g1 = from;
        while(_g1 < until) {
            let leaf = leaves[_g1++];
            let tmp=leaf.tmp;
            let diff=Method.subArray(tmp,centerMean,this._tv2,0,0,0,3);
            Method.multiplyArray(diff,diff,0,0,3,diff,0);
            Method.addArray(variance,diff,variance,0,0,0,3);
        }
        let lr=Method.setXYZ(this._tv2,from,until-1,0,0);
        if(variance[0] > variance[1]) {
            if(variance[0] > variance[2]) {
                this._setLeavesDir(centerMean[0],lr,leaves,0,0);
            } else {
                this._setLeavesDir(centerMean[2],lr,leaves,2,2);
            }
        } else if(variance[1] > variance[2]) {
            this._setLeavesDir(centerMean[1],lr,leaves,1,1);
        } else {
            this._setLeavesDir(centerMean[2],lr,leaves,2,2);
        }
        return lr[0];
    }
}